package mapper_core

import (
	"database/sql"
	"errors"
	"fmt"
	"reflect"
)

// 执行sql语句，返回插入的id
func DoExecWithLastInsertId(dbOrTxObj any, sqlStr string, args ...any) (int64, error) {

	switch v := dbOrTxObj.(type) {
	case *sql.DB:
		re1, er := v.Exec(sqlStr, args...)
		if er != nil {
			return 0, er
		}
		return re1.LastInsertId()
	case *sql.Tx:

		re1, er := v.Exec(sqlStr, args...)

		if er != nil {
			return 0, er
		}

		//处理返回的插入的id
		return re1.LastInsertId()

	default:
		return 0, errors.New("dbOrTxObj type error")
	}
}

// 执行sql语句，返回影响的行数
func DoExecWithEffectNum(dbObj any, sqlStr string, args ...any) (int64, error) {

	switch v := dbObj.(type) {
	case *sql.DB:
		re1, er := v.Exec(sqlStr, args...)
		if er != nil {
			return 0, er
		}
		return re1.RowsAffected()
	case *sql.Tx:

		re1, er := v.Exec(sqlStr, args...)

		if er != nil {
			return 0, er
		}
		return re1.RowsAffected()

	default:
		return 0, errors.New("dbOrTxObj type error")
	}
}

// 查询单个
func SelectOneMapBase(dbObj any, sqlSelectStr, sqlFromStr, sqlEndStr string, args []any) (map[string]any, error) {

	var t map[string]any = nil

	sqlStart := "SELECT "
	var rows *sql.Rows
	var err error

	switch v := dbObj.(type) { //不能使用QueryRow，因为QueryRow只能查询一条数据
	case *sql.DB:
		rows, err = v.Query(sqlStart+sqlSelectStr+sqlFromStr+sqlEndStr, args...)
	case *sql.Tx:
		rows, err = v.Query(sqlStart+sqlSelectStr+sqlFromStr+sqlEndStr, args...)
	default:
		return nil, errors.New("dbObj type error")
	}

	if rows != nil {
		defer rows.Close()
	}

	if err != nil {
		if err == sql.ErrNoRows { //无数据 不算错误
			return nil, nil
		}
		return nil, err
	}

	onlyOne := false
	haveResult := false
	for rows.Next() {
		haveResult = true
		if onlyOne {
			return nil, fmt.Errorf("%s", "too more result") //查询结果超过一条，抛出异常，防止数据不一致
		}
		columnTypes, err := rows.ColumnTypes()
		if err != nil {
			return nil, err
		}
		// 动态创建一个指针数组，用于存储 Scan 方法的参数
		values := make([]interface{}, len(columnTypes))
		for i := range columnTypes {

			if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullString{}) {
				var aa *string = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullTime{}) {
				var aa *string = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullBool{}) {
				var aa *bool = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullInt16{}) {
				var aa *int16 = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullInt32{}) {
				var aa *int32 = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullInt64{}) {
				var aa *int64 = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullFloat64{}) {
				var aa *float64 = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else {
				values[i] = reflect.New(columnTypes[i].ScanType()).Interface()
			}
		}

		err = rows.Scan(values...)
		if err != nil {
			return nil, err
		}
		t = make(map[string]any)
		for i, v := range values {

			if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullString{}) {

				if v.(**string) != nil {
					t[columnTypes[i].Name()] = *v.(**string)
				} else {
					var aa *string = nil
					t[columnTypes[i].Name()] = aa
				}
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullTime{}) {
				if v.(**string) != nil {
					t[columnTypes[i].Name()] = *v.(**string)
				} else {
					var aa *string = nil
					t[columnTypes[i].Name()] = aa
				}
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullBool{}) {
				if v.(**bool) != nil {
					t[columnTypes[i].Name()] = *v.(**bool)
				} else {
					var aa *bool = nil
					t[columnTypes[i].Name()] = aa
				}
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullInt16{}) {
				if v.(**int16) != nil {
					t[columnTypes[i].Name()] = *v.(**int16)
				} else {
					var aa *int16 = nil
					t[columnTypes[i].Name()] = aa
				}
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullInt32{}) {
				if v.(**int32) != nil {
					t[columnTypes[i].Name()] = *v.(**int32)
				} else {
					var aa *int32 = nil
					t[columnTypes[i].Name()] = aa
				}
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullInt64{}) {
				if v.(**int64) != nil {
					t[columnTypes[i].Name()] = *v.(**int64)
				} else {
					var aa *int64 = nil
					t[columnTypes[i].Name()] = aa
				}
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullFloat64{}) {
				if v.(**float64) != nil {
					t[columnTypes[i].Name()] = *v.(**float64)
				} else {
					var aa *float64 = nil
					t[columnTypes[i].Name()] = aa
				}
			} else {
				t[columnTypes[i].Name()] = v
			}
		}
		onlyOne = true
	}

	if !haveResult {
		return nil, nil
	}

	return t, nil

}

// 查询列表
func SelectListMapBase(dbObj any, sqlSelectStr, sqlFromStr, sqlEndStr string, args []any) ([]map[string]any, error) {

	sqlStart := "SELECT "

	var reList []map[string]any = make([]map[string]any, 0)

	var rows *sql.Rows
	var err error

	switch v := dbObj.(type) {
	case *sql.DB:
		rows, err = v.Query(sqlStart+sqlSelectStr+sqlFromStr+sqlEndStr, args...)
	case *sql.Tx:
		rows, err = v.Query(sqlStart+sqlSelectStr+sqlFromStr+sqlEndStr, args...)
	default:
		return nil, errors.New("dbObj type error")
	}

	if rows != nil {
		defer rows.Close()
	}

	if err != nil {
		if err == sql.ErrNoRows { //无数据 不算错误
			return nil, nil
		}
		return nil, err
	}

	for rows.Next() {
		t := make(map[string]any)
		columnTypes, err := rows.ColumnTypes()
		if err != nil {
			return nil, err
		}
		// 动态创建一个指针数组，用于存储 Scan 方法的参数
		values := make([]interface{}, len(columnTypes))
		for i := range columnTypes {

			if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullString{}) {
				var aa *string = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullTime{}) {
				var aa *string = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullBool{}) {
				var aa *bool = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullInt16{}) {
				var aa *int16 = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullInt32{}) {
				var aa *int32 = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullInt64{}) {
				var aa *int64 = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullFloat64{}) {
				var aa *float64 = nil
				values[i] = reflect.New(reflect.TypeOf(aa)).Interface()
			} else {
				values[i] = reflect.New(columnTypes[i].ScanType()).Interface()
			}
		}

		err = rows.Scan(values...)
		if err != nil {
			return nil, err
		}
		for i, v := range values {

			if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullString{}) {

				if v.(**string) != nil {
					t[columnTypes[i].Name()] = *v.(**string)
				} else {
					var aa *string = nil
					t[columnTypes[i].Name()] = aa
				}
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullTime{}) {
				if v.(**string) != nil {
					t[columnTypes[i].Name()] = *v.(**string)
				} else {
					var aa *string = nil
					t[columnTypes[i].Name()] = aa
				}
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullBool{}) {
				if v.(**bool) != nil {
					t[columnTypes[i].Name()] = *v.(**bool)
				} else {
					var aa *bool = nil
					t[columnTypes[i].Name()] = aa
				}
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullInt16{}) {
				if v.(**int16) != nil {
					t[columnTypes[i].Name()] = *v.(**int16)
				} else {
					var aa *int16 = nil
					t[columnTypes[i].Name()] = aa
				}
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullInt32{}) {
				if v.(**int32) != nil {
					t[columnTypes[i].Name()] = *v.(**int32)
				} else {
					var aa *int32 = nil
					t[columnTypes[i].Name()] = aa
				}
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullInt64{}) {
				if v.(**int64) != nil {
					t[columnTypes[i].Name()] = *v.(**int64)
				} else {
					var aa *int64 = nil
					t[columnTypes[i].Name()] = aa
				}
			} else if columnTypes[i].ScanType() == reflect.TypeOf(sql.NullFloat64{}) {
				if v.(**float64) != nil {
					t[columnTypes[i].Name()] = *v.(**float64)
				} else {
					var aa *float64 = nil
					t[columnTypes[i].Name()] = aa
				}
			} else {
				t[columnTypes[i].Name()] = v
			}

		}
		reList = append(reList, t)
	}

	return reList, nil

}
